﻿// Author: shees @ http://shz.no
// License: Microsoft Public License (Ms-PL)

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace Imshz.Repo.WebBenchmarkTools.WebBenchmarkConsole
{
    class Program
    {
        static void Main()
        {
            ConsoleKeyInfo appRunKey;

            do
            {
                ConsoleKeyInfo editUriKey;
                string uri;
                string useragent;

                #region Disclaimer
                Console.Clear();
                Console.WriteLine();
                Console.WriteLine(" Web Benchmark Tools v1.2 (http://webbenchmark.codeplex.com)");
                Console.WriteLine();
                Console.WriteLine(" Author: shees @ http://shz.no");
                Console.WriteLine(" License: Microsoft Public License (Ms-PL)");
                Console.WriteLine();
                #endregion

                #region Useragent
                do
                {
                    Console.WriteLine(" Type the number the user agent you want to use:");
                    Console.WriteLine(" 0 - Chrome 41.0.2228.0");
                    Console.WriteLine(" 1 - Internet Explorer 11.0 on 64-bit Windows 8.1 Update");
                    Console.WriteLine(" 2 - Internet Explorer for Windows Phone 8.1 Update");
                    Console.WriteLine(" 3 - Firefox 10.0 on Windows 8.1");
                    Console.WriteLine(" 4 - Firefox on Android");
                    Console.WriteLine(" 5 - iPhone OS 6.0 (Safari)");
                    Console.WriteLine();
                    Console.Write(" User agent no: ");
                    var key = Console.ReadKey();

                    switch (key.KeyChar)
                    {
                        case '0':
                            useragent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";
                            break;
                        case '1':
                            useragent = "Mozilla/5.0 (Windows NT 6.3; Win64, x64; Trident/7.0; Touch; rv:11.0) like Gecko";
                            break;
                        case '2':
                            useragent = "Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11; IEMobile/11.0) like Android 4.1.2; compatible) like iPhone OS 7_0_3 Mac OS X WebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Mobile Safari /537.36";
                            break;
                        case '3':
                            useragent = "Mozilla/5.0 (Windows NT 6.3; rv:10.0) Gecko/20100101 Firefox/10.0";
                            break;
                        case '4':
                            useragent = "Mozilla/5.0 (Android; Mobile; rv:26.0) Gecko/26.0 Firefox/26.0";
                            break;
                        case '5':
                            useragent = "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25";
                            break;
                        default:
                            useragent = string.Empty;
                            Console.WriteLine(" Invalid selection...Please retry!");
                            break;
                    }

                    Console.WriteLine();
                    Console.WriteLine();
                } while (string.IsNullOrWhiteSpace(useragent));
                #endregion

                #region Uri
                do
                {
                    Console.WriteLine(" Please type your webpage uri, and press 'enter':");
                    Console.Write(" Uri: ");
                    uri = Console.ReadLine();
                    
                    if (string.IsNullOrWhiteSpace(uri))
                    {
                        uri = "http://127.0.0.1";
                    }
                    else if (!(uri.ToLower().StartsWith("http://") 
                        || uri.ToLower().StartsWith("https://")))
                    {
                        uri = "http://" + uri;
                    }

                    Console.WriteLine();
                    Console.WriteLine(" You entered: \"" + uri + "\"");
                    Console.Write(" Type 'e' to edit entered uri, or press enter to continue...");
                    editUriKey = Console.ReadKey();
                    Console.WriteLine();
                    Console.WriteLine();
                } while (editUriKey.Key == ConsoleKey.E);

                #endregion

                var semaphore = new SemaphoreSlim(Environment.ProcessorCount, Environment.ProcessorCount);

                #region Benchmark options

                const int rounds = 10;
                const int requests = 10000;

                long ticks = 0;
                long milliseconds = 0;

                Console.WriteLine(" Benchmarking with " + rounds + " rounds of tests with " + requests + " requests per round:");
                Console.WriteLine();

                for (int i = 0; i < rounds; i++)
                {
                    var watch = Stopwatch.StartNew();

                    var tasks = new Task[requests];

                    for (int b = 0; b < requests; b++)
                    {
                        semaphore.Wait();

                        Task t = Task.Run(() =>
                        {
                            try
                            {
                                var request = (HttpWebRequest) WebRequest.Create(uri);
                                request.UserAgent = useragent;

                                using (var responseStream = new MemoryStream())
                                {
                                    using (var response = request.GetResponse())
                                    {
                                        using (var stream = response.GetResponseStream())
                                        {
                                            if (stream != null)
                                            {
                                                stream.CopyTo(responseStream);
                                                stream.Close();
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(" Error: " + ex.Message);
                            }
                            finally
                            {
                                semaphore.Release();
                            }
                        });

                        tasks[b] = t;
                    }

                    Task.WaitAll(tasks);

                    watch.Stop();

                    Console.Out.WriteAsync(" " + i + " - " + requests + " calls took " + watch.ElapsedMilliseconds + "ms (" +
                                           watch.ElapsedTicks + " ticks)" + Environment.NewLine);

                    ticks += watch.ElapsedTicks;
                    milliseconds += watch.ElapsedMilliseconds;
                }

                Console.WriteLine();
                Console.WriteLine(" Average time per " + requests + " calls : "
                                    + (milliseconds/rounds) + "ms (" + (ticks/rounds) + " ticks)");
                Console.WriteLine(" Average time per call : " + ((milliseconds/rounds)/requests)
                                    + "ms (" + ((ticks/rounds)/requests) + " ticks)");
                Console.WriteLine();
                Console.WriteLine();

                #endregion

                Console.WriteLine(" If you wish to restart the benchmarker type 'r',");
                Console.Write(" or type any other key to exit...");
                appRunKey = Console.ReadKey();
            } while (appRunKey.Key == ConsoleKey.R);
        }
    }
}
